/*
 * Visitable tree class downloaded off the internet, slightly modified.
 */

package cz.cuni.amis.pogamut.episodic.lispbots;

import java.io.IOException;
import java.util.*;

interface Visitor<T> {
    Visitor<T> visitTree(VisitableTree<T> tree);

    boolean visitData(VisitableTree<T> parent, T data, boolean isLeaf) throws java.io.IOException;
    boolean postVisitData(VisitableTree<T> parent, T data, boolean isLeaf) throws java.io.IOException;
}

interface Visitable<T> {
    boolean walk(Visitor<T> visitor) throws java.io.IOException;
}

class VisitableTree<T> implements Visitable<T> {
    // NB: LinkedHashSet preserves insertion order
    private final List<VisitableTree> children = new ArrayList<VisitableTree>();
    private final T data;
    private boolean topmost = false;

    VisitableTree(T data) {
        this.data = data;
    }
    VisitableTree(T data, boolean _topmost) {
        this(data);
        topmost = _topmost;
    }

    public boolean walk(Visitor<T> visitor) throws java.io.IOException {
        boolean isLeaf = children.isEmpty();
        // Avoid the topmost node. It's only virtual.

        if (!topmost) {
            // First visit.
            if (!visitor.visitData(this, data, isLeaf))
                return false;
        }

        for (VisitableTree child : children) {
            Visitor<T> childVisitor = visitor.visitTree(child);
            if (!child.walk(childVisitor))
                return false;
        }

        if (!topmost) {
            // Revisit after children.
            if (!children.isEmpty()) {
                if (!visitor.postVisitData(this, data, isLeaf))
                    return false;
            }
        }
        return true;
    }

    public T showdata() {
        return data;
    }

    public VisitableTree child(T data) {
        return child(new VisitableTree(data));
    }

    public VisitableTree child(VisitableTree<T> child) {
        children.add(child);
        return child;
    }
}
